#include "qrBasic.h"
#include <assert.h>


using namespace std;
using namespace cv;

// TODO move this odd declearation
namespace cv{
//extern void resize (InputArray src, OutputArray dst,Size dsize, double fx = 0, double fy = 0, int interpolation = 1);
}

/*
    The actual picture is larger than the one seen.
    More memory is allocated to avoid handling edge
    pixcels.
*/
qrBasic::qrBasic(int center, int border, string msg, string originalPic){
    c_size = center;
    b_size = border;
    f_size = 1;         // set the filter size, 3*3 filter
    p_size = c_size + 2 * b_size;
    message = msg;
    qrcode = NULL;
    img = NULL;
    oriImg = NULL;
    // gen QRcode
    qrcode = QRcode_encodeString(msg.data(), 0, QR_ECLEVEL_H, QR_MODE_8, 1);
    if(qrcode == NULL){
        clog::fl("Error Gen QR code");
        return;
    }
    width = qrcode->width * p_size;
    height = qrcode->width * p_size;
    clog::dl("finished generate qrcode");
    
    // gen Image
    IplImage *tmpImg = cvLoadImage(originalPic.data());
    if(tmpImg == NULL){
        clog::f("ERROR cannot find Image:");
        clog::fl(originalPic);
    }
    Mat tmpResize = cvarrToMat(tmpImg);
    float ratio;
    if (tmpImg->width > tmpImg->height) ratio = ((float)width) / tmpImg->width;
    else ratio = ((float)width) / tmpImg->height;
    Size resizeSize = Size((int)(tmpImg->width *ratio), (int)(tmpImg->height * ratio));
    cv::resize(tmpResize, tmpResize, resizeSize);
    // create dst Image;
    img = cvCreateImage(cvSize(width + f_size * 2, height + f_size * 2), IPL_DEPTH_8U, 3);
    img->imageData += f_size * img->nChannels + f_size * img->widthStep;
    img->width = width;
    img->height = height;
    // copy data to Dst Image
    memset(img->imageData, 0xff,img->imageSize);
    uchar * dstp = (uchar *)img->imageData;
    uchar * srcp = tmpResize.data;
    dstp += (img->width - resizeSize.width) / 2 * img->nChannels + (img->height - resizeSize.height) / 2 * img->widthStep;
    for(int i = 0; i < resizeSize.height; i++){
        memcpy(dstp,srcp,resizeSize.width * img->nChannels);
        dstp += img->widthStep;
        srcp += tmpResize.step[0];
    }
    // Set Initial Status
    for(int i = 0; i < width; i++)
    for(int j = 0; j < height; j++){
        if(isUnchangeable(i,j) || isCenter(i,j)){
           setPixcelAll(i,j,getColor(i,j));
        }
    }
}

qrBasic::~qrBasic(){
    if(qrcode)
        QRcode_free(qrcode);
    if(img)
        cvReleaseImage(&img);
}

uchar qrBasic::getColor(int x,int y){
    assert(x >= 0);
    assert(y >= 0);
    assert(x < width);
    assert(y < width);

    if (qrcode->data[qrcode->width * (y / p_size) + (x / p_size)] & 0x01){
        return 0;
    }else{
        return 255;
    }
}

bool qrBasic::isCenter(int x, int y){
    int xx = x % p_size;
    int yy = y % p_size;
    if(xx < b_size || yy < b_size)
        return false;
    if(xx > b_size+c_size || yy > b_size + c_size)
        return false;
    return true;
}

bool qrBasic::isUnchangeable(int x, int y){
    assert(x >= 0);
    assert(y >= 0);
    assert(x < width);
    assert(y < width);
    if (qrcode->data[qrcode->width * (y / p_size) + (x / p_size)] & 0x02){
        return false;
    }else{
        return true;
    }
}


int qrBasic::getHeight(){
    return height;
}

int qrBasic::getWidth(){
    return width;
}

void qrBasic::showPic(string windowName){
    cvShowImage(windowName.data(), img);
    cvWaitKey(0);
}


// Since this is Colored Pic, return a compacted value
// TODO update Gaussian filter value or other things.
uchar* qrBasic::getDataAt(int x, int y){
    assert(x > -f_size);
    assert(y > -f_size);
    assert(x < width + f_size);
    assert(y < height + f_size);
    return (uchar *)img->imageData + x * img->nChannels + y * img->widthStep;
}

uint qrBasic::setPixcelAll(int x, int y, uchar val){
    uchar * b = getDataAt(x,y);
    b[0] = val;
    b[1] = val;
    b[2] = val;
}

uint qrBasic::setPixcelR(int x, int y, uchar val){
    getDataAt(x,y)[2] = val;
}

uint qrBasic::setPixcelG(int x, int y, uchar val){
    getDataAt(x,y)[1] = val;
}

uint qrBasic::setPixcelB(int x, int y, uchar val){
    getDataAt(x,y)[0] = val;
}

uint qrBasic::setPixcelRGB(int x, int y, uint val){
    uchar * b = getDataAt(x,y);
    b[0] = val;
    b[1] = val >> 8;
    b[2] = val >> 16; 
}

uchar qrBasic::getPixcelR(int x, int y){
    return getDataAt(x,y)[2];
}

uchar qrBasic::getPixcelG(int x, int y){
    return getDataAt(x,y)[1];
}

uchar qrBasic::getPixcelB(int x, int y){
    return getDataAt(x,y)[0];
}

uint qrBasic::getPixcelRGB(int x, int y){
    uchar * b = getDataAt(x,y);
    uint val = 0;
    val |=  ((uint)b[0]) ;
    val |=  ((uint)b[1]) << 8;
    val |=  ((uint)b[2]) << 16;
    return val;
}

